

#include "msp430g2553.h"
#include "util.h"
#include "i2c_slave.h"

unsigned char 	rx_tx_buf[RX_TX_BUF_LEN];
short int		rx_tx_buf_cnt = 0;
short int		tx_buf_len = 0;
unsigned char 	reg_addr = 0;
short int		current_state = ST_NO_REG;

void	init_i2c_slave(unsigned char my_addr)
{
	short int i;
	
	for(i = 0; i < RX_TX_BUF_LEN; i++) {
		rx_tx_buf[i] = 0;
	}
	
	// USCI_B config, I2C Slave
	// set CTL1 first, to keep in reset while configuring everything else
	UCB0CTL1 = 	UCTR * 0 + 			// set as receiver
				UCSWRST;			// keep in reset
				
	UCB0CTL0 = 	UCA10 * 0 +			// 0 = use 7-bit addresses for own address
				UCMST * 0 +			// 0 = we are I2C slave
				UCMODE_3 +			// I2C mode
				UCSYNC * 1;			// synchronous operation

	UCB0I2COA = (UCGCEN * 0) + my_addr;		// UCGEN = 0 -> do not respond to general call
				
	SET_BIT8(P1SEL, 7);  // on g2553, UCB0SDA -> P1.7, P1SEL.7 = 1 and P1SEL2.7 = 1
	SET_BIT8(P1SEL2, 7); 
	
	SET_BIT8(P1SEL, 6);  // on g2553, UCB0SCL -> P1SEL.6 = 1 and P1SEL2.6 = 1
	SET_BIT8(P1SEL2, 6); 

	UCB0CTL1 &= ~UCSWRST;	// take out of reset (after configuring everything but before enabling interrupts)
	
	IE2 |= UCB0TXIE | UCB0RXIE;		// enable I2C interrupts on TX and RX
	UCB0I2CIE |= UCSTTIE | UCSTPIE;		// enable I2C interrupts for starts and stops
	
}


short int	state_machine(short int current_state, short int event)
{
	// documented at http://code.google.com/p/pibot/wiki/GenericI2CSlave#State_Machine
	if (current_state == ST_NO_REG  && event == EV_STR_RX) {
		// nothing
		return ST_RDY_ADDR;
	} else if (current_state == ST_RDY_ADDR  && event == EV_RX) {
		// Store register address
		reg_addr = UCB0RXBUF;
		rx_tx_buf_cnt = 0;
		return ST_REG_ADDR;
	} else if ((current_state == ST_REG_ADDR || current_state == ST_RX_REG) 
						&& event == EV_RX) {
		// Store data byte, increment counter (if too many NACK)
		if(rx_tx_buf_cnt >= RX_TX_BUF_LEN) {
			SET_NACK;
		} else {
			rx_tx_buf[rx_tx_buf_cnt] = UCB0RXBUF;
			rx_tx_buf_cnt++;	// I hate code that depends on ++i vs. i++
		}
		return ST_RX_REG;
	} else if (current_state == ST_REG_ADDR && event == EV_STR_TX) {
		// TX callback with register address	 
		tx_buf_len = TX_callback(reg_addr, rx_tx_buf);
		return ST_TX_REG;
	} else if (current_state == ST_TX_REG  && event == EV_TX) {
		// Send data byte, increment counter (if too many NACK)
		if(rx_tx_buf_cnt >= tx_buf_len) {
			UCB0TXBUF = 0xFF; // can't nack on TX
		} else {
			UCB0TXBUF = rx_tx_buf[rx_tx_buf_cnt];
			rx_tx_buf_cnt++;	// I hate code that depends on ++i vs. i++
		}	 
		return ST_TX_REG;
	} else if (current_state == ST_TX_REG  && event == EV_STP) {
		// (nothing)	 
		return ST_NO_REG;
	} else if (current_state == ST_RX_REG  && event == EV_STP) {
		// RX callback with data	 
		RX_callback(reg_addr, rx_tx_buf, rx_tx_buf_cnt);
		return ST_NO_REG;
	}	
	
	// if we get here, we didn't find a valid state/event pair, reset,
	// do stuff to clear various interrupt vectors.  Status interrupts are always
	// cleared even in funky conditions, but the TX/RX ones need to be cleared
	// to prevent microcontroller hanging.
	if(IFG2&UCB0TXIFG) {  
		UCB0TXBUF = 0xFF; // can't nack on TX
	}
	if(IFG2&UCB0RXIFG) {  // USCI_B0 requested RX interrupt (need to read a byte from UCB0RXBUF)
		SET_NACK;			// send NACK condition
	}
	return ST_NO_REG;	// reset state to beginning of transaction
}


// I2C RX/TX interrupt
// Note that in I2C mode the TX interrupt is used for BOTH RX and TX, and
// the RX interrupt is used for the status flags.  Sigh.
#pragma vector=USCIAB0TX_VECTOR
__interrupt void I2C_TX_RX(void)
{
	_BIC_SR(GIE); 						   // Disable global interrupt - nesting bad

	if(IFG2&UCB0TXIFG) {  // USCI_B0 requested TX interrupt (need to write a byte to UCB0TXBUF)
		current_state = state_machine(current_state, EV_TX);
	}
	if(IFG2&UCB0RXIFG) {  // USCI_B0 requested RX interrupt (need to read a byte from UCB0RXBUF)
		current_state = state_machine(current_state, EV_RX);
	}

	_BIS_SR(GIE); 						   // Enable global interrupt
}

// I2C RX interrupt - in I2C slave mode this is used for status interrupts only (*not* RX!)
#pragma vector=USCIAB0RX_VECTOR
__interrupt void I2C_Status(void)
{
	_BIC_SR(GIE); 						   // Disable global interrupt - nesting bad

	// we are in trouble if we get here and both bits are set... sigh.
	if(UCB0STAT&UCSTTIFG) {  // start condition
		if(UCB0CTL1&UCTR) { // transmitter
			current_state = state_machine(current_state, EV_STR_TX);
		} else {	// receiver
			current_state = state_machine(current_state, EV_STR_RX);
		}
		UCB0STAT &= ~UCSTTIFG;	// clear flag
	} else if(UCB0STAT&UCSTPIFG) {  // stop condition
		current_state = state_machine(current_state, EV_STP);
		UCB0STAT &= ~UCSTPIFG;	// clear flag
	}
	_BIS_SR(GIE); 						   // Enable global interrupt
}


